﻿/**
* @description  Class handles loading and playback of progressive FLV files.
*/
package com.foed.media.controllers {

	import flash.utils.Timer;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.TimerEvent;
	import flash.events.NetStatusEvent;
	import flash.events.ProgressEvent;
	import flash.net.NetConnection;
	import flash.net.NetStream;

	import com.foed.events.MediaEvent;
	
	public class FLVController extends MediaController {
	
		private var _stream_ns:NetStream;
		private var _connection_nc:NetConnection;
		private var _videoEnded:Boolean;
		private var _pauseTimer:Timer;
		private var _loadTimer:Timer;
	
		/**
		* @description  Handler for NetStream's status event.
		*
		* @param  pEvent  Event fired by NetStream.
		*/	
		private function onNetStatus(pEvent:NetStatusEvent):void {
			switch (pEvent.info.level) {
				case "error":
					dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
					break;
				case "status":
					switch (pEvent.info.code) {
						case "NetStream.Play.Start":
							dispatchEvent(new Event(Event.INIT));
							break;
						case "NetStream.Play.Stop":
							_videoEnded = true;
							break;
					}
					break;
			}
		}

		/**
		* @description  Handler for timer during downloading of video to assess load progress.
		*
		* @param  pEvent  Event fired by Timer.
		*/
		private function assessLoad(pEvent:TimerEvent):void {
			dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS, false, false, _stream_ns.bytesLoaded, _stream_ns.bytesTotal));
			if (_stream_ns.bytesLoaded == _stream_ns.bytesTotal) {
				dispatchEvent(new Event(Event.COMPLETE));
				_loadTimer.removeEventListener(TimerEvent.TIMER, assessLoad);
				_loadTimer.stop();
			}
		}

		/**
		* @description  Called at an interval as the media is played back.
		*
		* @param  pEvent  The event fired by the Timer.
		*/		
		override protected function trackProgress(pEvent:Event):void {
			if (mediaPosition == mediaDuration || _videoEnded) {
				_videoEnded = false;
				stopMedia();
				dispatchEvent(new MediaEvent(MediaEvent.COMPLETE, mediaPosition, mediaDuration));
			}
			super.trackProgress(pEvent);
		}

		/**
		* @description  Applies current sound settings to media.
		*/		
		override protected function applySoundTransform():void {
			_stream_ns.soundTransform = getSoundTransform();
		}

		/**
		* @description  Triggers the request and loading of the controller's media.
		*
		* @param  pFileURL  The http or rtmp path to the file to load.
		*/		
		override public function loadMedia(pFileURL:String):void {
			super.loadMedia(pFileURL);
			_connection_nc = new NetConnection();
			try {
				_connection_nc.connect(null);
				_stream_ns = new NetStream(_connection_nc);
				_stream_ns.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus, false, 0, true);
				_stream_ns.client = this;
				applySoundTransform();
				dispatchEvent(new MediaEvent(MediaEvent.LOAD));
				startMedia();
			} catch (e:Error) {}
		}
	
		/**
		* @description  Begins playback of the loaded media.
		*/		
		override public function startMedia():void {
			_videoEnded = false;
			if (paused) {
				_stream_ns.resume();
			} else {
				try {
					_stream_ns.play(mediaFile);
					if (_stream_ns.bytesLoaded != _stream_ns.bytesTotal && _stream_ns.bytesTotal > 0) {
						_loadTimer = new Timer(500, 0);
						_loadTimer.addEventListener(TimerEvent.TIMER, assessLoad, false, 0, true);
						_loadTimer.start();
					} 
				} catch (e:Error) {}
			}
			super.startMedia();
		} 
	
		/**
		* @description  Sends the media to the specified position, in milliseconds.
		*
		* @param  pOffset  The position in milliseconds to send the media to.
		*/
		override public function seek(pOffset:Number):void {
			_videoEnded = false;
			var pTime:int = Math.round(pOffset*1000)/1000;
			_stream_ns.seek(pTime);
			super.seek(pTime);
		}
	
		/**
		* @description  Halts playback of media.
		*/		
		override public function stopMedia():void {
			_stream_ns.seek(0);
			_stream_ns.pause();
			super.stopMedia();
		} 
	
		/**
		* @description  Pauses or resumes playback of media.
		*
		* @param  pPause  True to pause media. False to resume.
		*/		
		override public function pauseMedia(pPause:Boolean):void {
			if (pPause) {
				_stream_ns.pause();
			} else {
				if (!playing) _stream_ns.resume();
			}
			super.pauseMedia(pPause);
		}
	
		/**
		* @description  Destructor cleans up controller.
		*/		
		override public function die():void {
			super.die();
			_stream_ns.close();
			_connection_nc.close();
			if (_loadTimer.running) _loadTimer.stop();
		}

		/**
		* @description  Handler for embedded cue points in video. Unused.
		*
		* @param  pData  Cuepoint embedded in FLV.
		*/
		public function onCuePoint(pData:Object):void {}

		/**
		* @description  Handler for metadata embedded in video.
		*
		* @param  pData  Object containing duration, width and height info.
		*/
		public function onMetaData(pData:Object):void {
			if (pData.duration > 0) mediaDuration = pData.duration;
			dispatchEvent(new MediaEvent(MediaEvent.METADATA, mediaPosition, mediaDuration, pData.width, pData.height));
		}
		
		/**
		* @description  Returns the current position, in milliseconds, of the media.
		*
		* @returns  The current position, in milliseconds, of the media.
		*/		
		override public function get mediaPosition():Number { return _stream_ns.time; }
	
		/**
		* @description  Returns the media playback object for the controller.
		*
		* @returns  A reference to the object containing the media.
		*/		
		override public function get media():Object { return _stream_ns; }

	}
	
}